﻿using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.WorkItemManager.UI.Entities
{
    public sealed class OverView : global::Sidvall.WorkItemManager.UI.Entities.Core.BusinessBase
    {
        #region Public Members

        #region ExecutingWorkItemCount

        private long _ExecutingWorkItemCount = 0;
        public long ExecutingWorkItemCount
        {
            get
            {
                return _ExecutingWorkItemCount;
            }
            set
            {
                _ExecutingWorkItemCount = value;
                OnPropertyChanged("ExecutingWorkItemCount");
            }
        }

        #endregion
        #region WorkerCount

        private long _WorkerCount = 0;
        public long WorkerCount
        {
            get
            {
                return _WorkerCount;
            }
            set
            {
                _WorkerCount = value;
                OnPropertyChanged("WorkerCount");
            }
        }

        #endregion
        #region WorkItemCount

        private long _WorkItemCount = 0;
        public long WorkItemCount
        {
            get
            {
                return _WorkItemCount;
            }
            set
            {
                _WorkItemCount = value;
                OnPropertyChanged(nameof(WorkItemCount));
            }
        }

        #endregion
        #region WorkItemLogCount

        private long _WorkItemLogCount = 0;
        public long WorkItemLogCount
        {
            get
            {
                return _WorkItemLogCount;
            }
            set
            {
                _WorkItemLogCount = value;
                OnPropertyChanged(nameof(WorkItemLogCount));
            }
        }

        #endregion
        #region LastExecutedItems

        private System.Collections.Generic.IEnumerable<WorkItem> _LastExecutedItems;
        public System.Collections.Generic.IEnumerable<WorkItem> LastExecutedItems
        {
            get
            {
                return _LastExecutedItems;
            }
            set
            {
                _LastExecutedItems = value;
                OnPropertyChanged("LastExecutedItems");
            }
        }

        #endregion
        #region LastFailedItems

        private System.Collections.Generic.IEnumerable<WorkItem> _LastFailedItems;
        public System.Collections.Generic.IEnumerable<WorkItem> LastFailedItems
        {
            get
            {
                return _LastFailedItems;
            }
            set
            {
                _LastFailedItems = value;
                OnPropertyChanged("LastFailedItems");
            }
        }

        #endregion
        #region LastMessageLogItems

        private System.Collections.Generic.IEnumerable<WorkItemLog> _LastMessageLogItems;
        public System.Collections.Generic.IEnumerable<WorkItemLog> LastMessageLogItems
        {
            get
            {
                return _LastMessageLogItems;
            }
            set
            {
                _LastMessageLogItems = value;
                OnPropertyChanged(nameof(LastMessageLogItems));
            }
        }

        #endregion

        #region ReloadAsync

        public async Task ReloadAsync()
        {
            // Prepare tasks
            var dataFetchResults = new System.Collections.ObjectModel.Collection<DataFetchResult>();
            foreach (var serverManager in Sidvall.WorkItemManager.UI.SystemContext.Current.ServerManagers.ValidItems)
            {
                var serverConnection = (from o in Sidvall.WorkItemManager.SystemContext.Current.UserConfiguration.ServerConnections.Items
                                        where o.ServerConnectionId == serverManager.ServerConnectionId
                                        select o).FirstOrDefault();
                var dataFetchResult = new DataFetchResult()
                {
                    ServerManager = serverManager,
                    ServerConnection = serverConnection,
                };
                dataFetchResults.Add(dataFetchResult);
            }

            // Create tasks
            var tasks = new System.Collections.Generic.List<System.Threading.Tasks.Task>();
            var iMax = dataFetchResults.Count;
            for (int i = 0; i < iMax; i++)
            {
                tasks.Add(ReloadAsync(dataFetchResults, i));
            }
            if (tasks.Count > 0)
                await System.Threading.Tasks.Task.WhenAll(tasks);

            // Reload items
            var executingWorkItemCount = 0L;
            var workerCount = 0L;
            var workItemCount = 0L;
            var workItemLogCount = 0L;
            var lastExecutedItems = new WorkItemCollection();
            var lastFailedItems = new WorkItemCollection();
            var lastMessageLogItems = new WorkItemLogCollection();
            iMax = dataFetchResults.Count;
            for (int i = 0; i < iMax; i++)
            {
                var dataFetchResult = dataFetchResults[i];
                if (dataFetchResult.Exception != null)
                    continue;
                var workIds = new System.Collections.Generic.HashSet<long>();
                var executingIds = new System.Collections.Generic.HashSet<long>();
                foreach (var workItem in dataFetchResult.WorkItems)
                {
                    workItem.Name = dataFetchResult.ServerConnection.Name + FullNameSeparator + workItem.Worker.Name + FullNameSeparator + workItem.Name;
                    if (workItem.LastExecutionEndTime != null)
                    {
                        lastExecutedItems.Add(workItem, 5);
                        if (workItem.LastWorkItemResultId == Sidvall.WorkItemManager.Workflow.WorkItemResultType.Failed)
                            lastFailedItems.Add(workItem, 5);
                    }
                    if (!workIds.Contains(workItem.WorkerId))
                        workIds.Add(workItem.WorkerId);
                    if (workItem.WorkItemStatusId == Workflow.WorkItemStatus.Executing)
                    {
                        if (!executingIds.Contains(workItem.WorkItemId.Value))
                            executingIds.Add(workItem.WorkItemId.Value);
                    }
                }
                foreach (var workItemLog in dataFetchResult.WorkItemLogs)
                {
                    var workitem = (from o in dataFetchResult.WorkItems
                                    where o.WorkItemId == workItemLog.WorkItemId
                                    select o).FirstOrDefault();
                    workItemLog.WorkItemName = workitem.Name;
                    lastMessageLogItems.Add(workItemLog, 10);
                }
                executingWorkItemCount += executingIds.Count;
                workerCount += workIds.Count;
                workItemCount += dataFetchResult.WorkItems.Count;
                workItemLogCount += dataFetchResult.WorkItemLogs.Count;
            }
            this.ExecutingWorkItemCount = executingWorkItemCount;
            this.WorkerCount = workerCount;
            this.WorkItemCount = workItemCount;
            this.WorkItemLogCount = workItemLogCount;
            this.LastExecutedItems = (from o in lastExecutedItems
                                      orderby o.LastExecutionEndTime descending
                                      select o);
            this.LastFailedItems = (from o in lastFailedItems
                                    orderby o.LastExecutionEndTime descending
                                    select o);
            this.LastMessageLogItems = (from o in lastMessageLogItems
                                        orderby o.ExecutionStartTime descending
                                        select o);
        }
        private async Task ReloadAsync(System.Collections.ObjectModel.Collection<DataFetchResult> dataFetchResults, int index)
        {
            var dataFetchResult = dataFetchResults[index];
            try
            {
                var workItemCriteria = ParameterManager.GetWorkItemFetchListCriteria(dataFetchResult.ServerConnection.ServerConnectionId.Value);
                workItemCriteria.IncludedEntities = new Sidvall.Data.RelationCollection<Data.Relations.WorkItemRelation>();
                workItemCriteria.IncludedEntities.Add(Data.Relations.WorkItemRelation.Worker);
                workItemCriteria.SortItems = new Sidvall.Data.SortItemCollection<Data.Fields.WorkItemField>();
                workItemCriteria.SortItems.Add(Data.Fields.WorkItemField.WorkerId, true);
                workItemCriteria.SortItems.Add(Data.Fields.WorkItemField.ExecutionOrder, true);
                dataFetchResult.WorkItems = await Sidvall.WorkItemManager.UI.SystemContext.Current.DataPortal.WorkItemManager.GetItemsAsync(workItemCriteria);

                var workItemLogCriteria = ParameterManager.GetWorkItemLogFetchListCriteria(dataFetchResult.ServerConnection.ServerConnectionId.Value);
                workItemLogCriteria.RowCount = 10;
                workItemLogCriteria.RowStartIndex = 0;
                workItemLogCriteria.SortItems = new Sidvall.Data.SortItemCollection<Data.Fields.WorkItemLogField>();
                workItemLogCriteria.SortItems.Add(Data.Fields.WorkItemLogField.ExecutionStartTime, false);
                workItemLogCriteria.Filter = new Sidvall.Data.Filter<Data.Fields.WorkItemLogField>();
                workItemLogCriteria.Filter.AddFilterItem(Data.Fields.WorkItemLogField.ResultMessage, Sidvall.Data.ComparisonOperator.NotEqual, string.Empty);
                dataFetchResult.WorkItemLogs = await Sidvall.WorkItemManager.UI.SystemContext.Current.DataPortal.WorkItemLogManager.GetItemsAsync(workItemLogCriteria);

                dataFetchResult.Exception = null;
            }
            catch (System.Exception ex)
            {
                dataFetchResult.Exception = ex;
            }
        }

        #endregion

        #endregion
        #region Private Members

        private const string FullNameSeparator = "/";

        #endregion
        #region Constructors

        public OverView()
        {
        }

        public OverView(Sidvall.Data.DataRowState? dataRowState)
            : base(dataRowState)
        {
        }

        #endregion
    }
}